﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Data;

namespace tabtool
{
    class CodeGen
    {
        public static string space_name = "game_data";

        #region CPP

        public static void MakeCppFile(List<TableMeta> metalist, string codepath)
        {
            foreach (var meta in metalist)
            {
                string hfile = codepath + meta.GetClassName() + ".hpp";
                using (FileStream fs = new FileStream(hfile, FileMode.Create, FileAccess.Write))
                {
                    using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8))
                    {
                        sw.WriteLine("//THIS FILE IS GENERATED BY tabtool, DO NOT EDIT IT!");
                        sw.WriteLine("//GENERATE TIME [{0}]", System.DateTime.Now.ToString());
                        sw.WriteLine("#pragma once");
                        sw.WriteLine("# include \"readtablefield.h\"");
                        sw.WriteLine("# include \"tablestruct.h\"");
                        sw.WriteLine();
                        sw.WriteLine("namespace {0} {{", space_name);
                        sw.WriteLine();
                        foreach (var field in meta.Fields)
                        {
                            string relate_info = field.Get("relate");
                            string regroup_info = field.Get("regroup");
                            if (relate_info != null && regroup_info != null)
                            {
                                throw new Exception(meta.TableName + " relate error!!! field name " + field.fieldName + "relate and regroup can`t both !!!");
                            }
                            if (relate_info != null)
                            {
                                string[] relates = relate_info.Split('|');
                                foreach (var relate in relates)
                                {
                                    string[] s = relate.Split('.');
                                    if (s.Count() != 2)
                                    {
                                        throw new Exception(meta.TableName + " relate error!!! field name " + field.fieldName);
                                    }
                                    sw.WriteLine("struct td_{0}_item;", s[0].ToLower());
                                }
                                meta.relate = true;
                            }
                            else if (regroup_info != null)
                            {
                                meta.regroup = true;
                            }
                        }
                        sw.WriteLine("struct {0} {{", meta.GetItemName());
                        foreach (var field in meta.Fields)
                        {
                            sw.WriteLine("  {0} {1};", field.GetCppTypeName(), field.fieldName);
                        }
                        foreach (var field in meta.Fields)
                        {
                            string relate_info = field.Get("relate");
                            if (relate_info != null && field.Get("regroup") == null)
                            {
                                string[] relates = relate_info.Split('|');
                                foreach (var relate in relates)
                                {
                                    string[] s = relate.Split('.');
                                    sw.WriteLine("  std::vector<const td_{0}_item*> __relate__td_{0}_items;", s[0].ToLower());
                                }
                            }
                        }

                        sw.WriteLine("};");
                        sw.WriteLine();
                        sw.WriteLine("class {0} : public IConfigTable<{1}>{{", meta.GetClassName(), meta.GetItemName());
                        sw.WriteLine("public:");
                        sw.WriteLine("virtual bool Load() {");
                        sw.WriteLine("\tReadTableFile reader;");
                        sw.WriteLine("\treader.Initialize();");
                        sw.WriteLine("\tif (!reader.Init(GetTableFile().c_str()))");
                        sw.WriteLine("\t\treturn false;");
                        sw.WriteLine("\ttry {");
                        sw.WriteLine("\t\tDataReader dr;");
                        sw.WriteLine("\t\tint iRows = reader.GetRowCount();");
                        sw.WriteLine("\t\tint iCols = reader.GetColCount();");
                        sw.WriteLine("\t\tfor (int i = 1; i < iRows; ++i) {");
                        sw.WriteLine("\t\t\t{0} item;", meta.GetItemName());
                        foreach (var field in meta.Fields)
                        {
                            switch (field.fieldType)
                            {
                                case TableFieldType.IntField:
                                case TableFieldType.EnumField:
                                case TableFieldType.MaskField:
                                    sw.WriteLine("\t\t\titem.{0} = dr.GetValue<int32_t>(reader.GetValue(i, \"{0}\"));", field.fieldName);
                                    break;
                                case TableFieldType.Int64Field:
                                    sw.WriteLine("\t\t\titem.{0} = dr.GetValue<int64_t>(reader.GetValue(i, \"{0}\"));", field.fieldName);
                                    break;
                                case TableFieldType.FloatField:
                                    sw.WriteLine("\t\t\titem.{0} = dr.GetValue<float>(reader.GetValue(i, \"{0}\"));", field.fieldName);
                                    break;
                                case TableFieldType.DoubleField:
                                    sw.WriteLine("\t\t\titem.{0} = dr.GetValue<double>(reader.GetValue(i, \"{0}\"));", field.fieldName);
                                    break;
                                case TableFieldType.StringField:
                                    sw.WriteLine("\t\t\titem.{0} = (reader.GetValue(i, \"{0}\"));", field.fieldName);
                                    break;
                                case TableFieldType.TupleField:
                                    //TODO:
                                    break;
                                case TableFieldType.StructField:
                                    sw.WriteLine("\t\t\titem.{0} = dr.GetObject<{1}>(reader.GetValue(i, \"{0}\"));", field.fieldName, field.GetCppTypeName());
                                    break;
                                case TableFieldType.ListField:
                                    if (field.subType == TableFieldType.StructField)
                                    {
                                        sw.WriteLine("\t\t\titem.{0} = dr.GetObjectList<{1}>(reader.GetValue(i, \"{0}\"));", field.fieldName, field.realType);
                                    }
                                    else
                                    {
                                        sw.WriteLine("\t\t\titem.{0} = dr.GetList<{1}>(reader.GetValue(i, \"{0}\"));", field.fieldName, field.FieldTypeToString(field.subType));
                                    }
                                    break;
                                case TableFieldType.MapField:
                                    break;
                            }
                        }
                        sw.WriteLine("\t\t\tm_Items[item.{0}] = item;", meta.Fields[0].fieldName);//必须有一个id
                        sw.WriteLine("\t\t}");
                        sw.WriteLine("\t} catch(std::exception& e) {");
                        sw.WriteLine("\t\tstd::cerr << e.what() << std::endl;");
                        sw.WriteLine("\t\treturn false;");
                        sw.WriteLine("\t}");
                        sw.WriteLine("\treturn true;");
                        sw.WriteLine("}");
                        sw.WriteLine("string GetTableFile() {");
                        sw.WriteLine("\tstring f = WORK_DIR;");
                        sw.WriteLine("\tf = f + \"{0}.txt\";", meta.TableName);
                        sw.WriteLine("\treturn f;");
                        sw.WriteLine("}");
                        sw.WriteLine("};");
                        sw.WriteLine("}");
                        sw.WriteLine();
                        if (meta.regroup)
                        {
                            sw.WriteLine("//regroup");
                            foreach (var f in meta.Fields)
                            {
                                string regroup_info = f.Get("regroup");
                                if (regroup_info == null) continue;
                                sw.WriteLine("extern {1}::{0} the_{0};", meta.GetClassName(), space_name);
                                sw.WriteLine("namespace {0} {{", space_name);
                                sw.WriteLine("class {0} : public IConfigTable<std::vector<const {1}*>>{{", "td_regroup_" + regroup_info.ToLower(), meta.GetItemName());
                                sw.WriteLine("public:");
                                sw.WriteLine("virtual bool Load() {");
                                sw.WriteLine("\tconst auto& base_table = the_{0}.RawMap();", meta.GetClassName());
                                sw.WriteLine("\t\tfor(auto& it : base_table) {");
                                sw.WriteLine("\t\t\tm_Items[it.second.{0}].push_back(&it.second);", f.fieldName);
                                sw.WriteLine("\t\t}");
                                sw.WriteLine("\t\treturn true;");
                                sw.WriteLine("\t}");
                                sw.WriteLine("};");
                                sw.WriteLine("}");
                            }
                        }
                        sw.WriteLine();

                    }
                }
            }

            string cppfile = codepath + "table_load.cpp";
            using (FileStream fs = new FileStream(cppfile, FileMode.Create, FileAccess.Write))
            {
                using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8))
                {
                    sw.WriteLine("//THIS FILE IS GENERATED BY tabtool, DO NOT EDIT IT!");
                    sw.WriteLine("//GENERATE TIME [{0}]", System.DateTime.Now.ToString());
                    sw.WriteLine("#include <iostream>");
                    sw.WriteLine();
                    foreach (var meta in metalist)
                    {
                        sw.WriteLine("#include \"{0}.hpp\"", meta.GetClassName());
                        sw.WriteLine("{0}::{1} the_{1};", space_name, meta.GetClassName());
                        sw.WriteLine("template<> ::{0}::{1} const& GET_TABLE<::{0}::{1}>() {{", space_name, meta.GetClassName());
                        sw.WriteLine("\treturn the_{0};", meta.GetClassName());
                        sw.WriteLine("}");
                        if (meta.regroup)
                        {
                            foreach (var f in meta.Fields)
                            {
                                string regroup_info = f.Get("regroup");
                                if (regroup_info == null) continue;
                                sw.WriteLine("{0}::{1} the_{1};", space_name, "td_regroup_" + regroup_info.ToLower());
                                sw.WriteLine("template<> ::{0}::{1} const& GET_TABLE<::{0}::{1}>() {{", space_name, "td_regroup_" + regroup_info.ToLower());
                                sw.WriteLine("\treturn the_{0};", "td_regroup_" + regroup_info.ToLower());
                                sw.WriteLine("}");
                            }
                        }
                    }
                    sw.WriteLine();
                    sw.WriteLine("namespace {0} {{", space_name);
                    sw.WriteLine("bool load_all_data() {");
                    foreach (var meta in metalist)
                    {
                        //序列化版本 使用exception机制
                        //sw.WriteLine("try {{ if(the_{0}.Load() == false) throw std::logic_error(\"{0} load failed !!!\"); }} catch (std::exception& e) {{ std::cerr << e.what(); throw e;}}", meta.GetClassName());
                        //非序列化版本
                        sw.WriteLine("\tif(!the_{0}.Load()) {{ std::cerr << \"load {0} failed !!!\" << std::endl; return false; }}", meta.GetClassName());
                        if (meta.regroup)
                        {
                            foreach (var f in meta.Fields)
                            {
                                string regroup_info = f.Get("regroup");
                                if (regroup_info == null) continue;
                                sw.WriteLine("\tif(!the_td_regroup_{0}.Load()) {{ std::cerr << \"load {0} failed !!!\" << std::endl; return false; }}", regroup_info.ToLower());
                            }
                        }
                    }
                    //relate code
                    sw.WriteLine("\t//relate code");
                    foreach (var meta in metalist)
                    {
                        if (meta.relate)
                        {
                            sw.WriteLine("\t{");
                            foreach (var field in meta.Fields)
                            {
                                string relate_info = field.Get("relate");
                                if (relate_info != null)
                                {
                                    string[] realtes = relate_info.Split('|');
                                    foreach (var realte in realtes)
                                    {
                                        string[] s = realte.Split('.');
                                        sw.WriteLine("\t\tfor(auto p1 = the_{0}.RawMap().begin(); p1 != the_{0}.RawMap().end(); ++p1) {{", meta.GetClassName());
                                        sw.WriteLine("\t\t\tauto& v1 = ({0}&)p1->second;", meta.GetItemName());
                                        sw.WriteLine("\t\t\tauto const& k1 = v1.{0};", field.fieldName);
                                        sw.WriteLine("\t\t\tfor(auto p2 = the_td_{0}.RawMap().begin(); p2 != the_td_{0}.RawMap().end(); ++p2) {{", s[0].ToLower());
                                        sw.WriteLine("\t\t\t\tauto const& v2 = p2->second;");
                                        sw.WriteLine("\t\t\t\tauto const& k2 = v2.{0};", s[1].ToLower());
                                        sw.WriteLine("\t\t\t\tif(k1 == k2) {");
                                        sw.WriteLine("\t\t\t\t\tv1.__relate__td_{0}_items.push_back(&v2);", s[0].ToLower());
                                        sw.WriteLine("\t\t\t\t}");
                                        sw.WriteLine("\t\t\t}");
                                        sw.WriteLine("\t\t}");
                                    }
                                }
                            }
                            sw.WriteLine("\t}");
                        }
                    }
                    sw.WriteLine("\treturn true;");
                    sw.WriteLine("}");
                    sw.WriteLine("}");
                }
            }
        }
        public static void MakeCppFileTbs(List<TableMeta> metalist, string codepath)
        {
            string hfile = codepath + "tablestruct.h";
            using (FileStream fs = new FileStream(hfile, FileMode.Create, FileAccess.Write))
            {
                using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8))
                {
                    sw.WriteLine("//THIS FILE IS GENERATED BY tabtool, DO NOT EDIT IT!");
                    sw.WriteLine("//GENERATE TIME [{0}]", System.DateTime.Now.ToString());
                    sw.WriteLine("#pragma once");
                    sw.WriteLine("#include \"readtablefield.h\"");
                    sw.WriteLine("#include \"myconfig.h\"");
                    sw.WriteLine();
                    sw.WriteLine("namespace {0} {{", space_name);

                    foreach (var meta in metalist)
                    {
                        sw.WriteLine();
                        sw.WriteLine("struct {0} : public ITableObject<{0}>", meta.TableName);
                        sw.WriteLine("{");
                        foreach (var field in meta.Fields)
                        {
                            sw.WriteLine("	{0} {1};", field.GetCppTypeName(), field.fieldName);
                        }
                        sw.WriteLine();
                        sw.WriteLine("	virtual bool FromString(string s)");
                        sw.WriteLine("	{");
                        sw.WriteLine("		DataReader dr;");
                        sw.WriteLine("		vector<string> vs = dr.GetStringList(s,',');");
                        sw.WriteLine("		if (vs.size() != {0})", meta.Fields.Count());
                        sw.WriteLine("		{");
                        sw.WriteLine("			ErrorLog(\"{0}字段配置错误\");", meta.TableName);
                        sw.WriteLine("			return false;");
                        sw.WriteLine("		}");
                        for (int i = 0; i < meta.Fields.Count(); i++)
                        {
                            var field = meta.Fields[i];
                            switch (field.fieldType)
                            {
                                case TableFieldType.IntField:
                                    sw.WriteLine("		{0} = dr.GetValue<int32_t>(vs[{1}]);", field.fieldName, i);
                                    break;
                                case TableFieldType.Int64Field:
                                    sw.WriteLine("		{0} = dr.GetValue<int64_t>(vs[{1}]);", field.fieldName, i);
                                    break;
                                case TableFieldType.FloatField:
                                    sw.WriteLine("		{0} = dr.GetValue<float>(vs[{1}]);", field.fieldName, i);
                                    break;
                                case TableFieldType.DoubleField:
                                    sw.WriteLine("		{0} = dr.GetValue<double>(vs[{1}]);", field.fieldName, i);
                                    break;
                                case TableFieldType.StringField:
                                    sw.WriteLine("		{0} = (vs[{1}]);", field.fieldName, i);
                                    break;
                                default:
                                    Console.WriteLine("{0}.{1}字段类型错误，只能是int float string！", meta.TableName, field.fieldName);
                                    break;
                            }
                        }
                        sw.WriteLine("		return true;");
                        sw.WriteLine("	}");
                        sw.WriteLine("};");
                        sw.WriteLine();
                    }
                    sw.WriteLine("}");
                    ////////////////////////

                }
            }
        }

        public static void MakeCppEnumAndMask(string excel_path, string codepath)
        {
            List<string> files = new List<string>();
            files.AddRange(Directory.GetFiles(excel_path + "/Enum/", "*.xlsm", SearchOption.TopDirectoryOnly));
            files.AddRange(Directory.GetFiles(excel_path + "/Mask/", "*.xlsm", SearchOption.TopDirectoryOnly));
            ExcelHelper helper = new ExcelHelper();
            foreach (string filepath in files)
            {
                try
                {
                    DataTable dt = helper.ImportExcelFile(filepath);
                    string filename = Path.GetFileNameWithoutExtension(filepath).ToLower();
                    string path = codepath + filename + ".h";
                    using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write))
                    {
                        StreamWriter sw = new StreamWriter(fs, Encoding.UTF8);
                        sw.WriteLine("//THIS FILE IS GENERATED BY tabtool, DO NOT EDIT IT!");
                        sw.WriteLine("//GENERATE TIME [{0}]", System.DateTime.Now.ToString());
                        sw.WriteLine("#pragma once");
                        sw.WriteLine("namespace {0} {{", filename);
                        sw.WriteLine("\tenum {0} {{", filename);
                        for (int i = 0; i < dt.Rows.Count; i++)
                        {
                            sw.WriteLine("\t\t{0} = {1}, //{2}", dt.Rows[i].ItemArray[1].ToString(), dt.Rows[i].ItemArray[0].ToString().ToLower(), dt.Rows[i].ItemArray[2].ToString().ToLower());
                        }
                        sw.WriteLine("\t};");
                        sw.WriteLine("\tinline char const* {0}_to_string(int val) {{", filename);
                        sw.WriteLine("\t\tswitch(val) {");
                        for (int i = 0; i < dt.Rows.Count; i++)
                        {
                            sw.WriteLine("\t\t\tcase {0}:{{ return \"{1}\";}}", dt.Rows[i].ItemArray[1].ToString(), dt.Rows[i].ItemArray[1].ToString().ToLower());
                        }
                        sw.WriteLine("\t\t\tdefault:{ return \"\";}");
                        sw.WriteLine("\t\t}");
                        sw.WriteLine("\t};");
                        sw.WriteLine("}");
                        sw.Close();
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(filepath + " 文件出错！");
                    Console.WriteLine(e.ToString());
                }
            }
            Console.WriteLine("<生成C++枚举掩码文件成功>");
        }

        #endregion

        #region CSharp

        /// <summary>
        /// 字段模板
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="typeName"></param>
        /// <param name="valueName"></param>
        /// <returns></returns>
        private static string GetValueModel(string msg, string typeName, string valueName)
        {
            string result = "";
            result += "        /// <summary>\r\n";
            result += "        /// " + msg + "\r\n";
            result += "        /// </summary>\r\n";
            result += "        public " + typeName + " " + valueName + " { get; private set; }\r\n";
            return result;
        }

        /// <summary>
        /// 文件名称
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="content"></param>
        /// <returns></returns>
        private static string GetFileModel(string fileName, string content)
        {
            string result = "namespace " + space_name + "\r\n";
            result += "{\r\n";
            result += "    public class " + fileName + " : ITableBase\r\n";
            result += "    {\r\n";
            result += content;
            result += "    }\r\n";
            result += "}";
            return result;
        }

        /// <summary>
        /// 获取Csharp单个枚举代码模板
        /// </summary>
        /// <param name="enumName">枚举名称</param>
        /// <param name="rows">DataTable的行</param>
        /// <returns></returns>
        private static string GetCsharpEnumModel(string enumName, DataRowCollection rows)
        {
            string result = "";
            result += "    public enum " + enumName + "\r\n";
            result += "    {\r\n";
            for (int i = 0; i < rows.Count; i++)
            {
                result += "        /// <summary>\r\n";
                result += "        /// " + rows[i].ItemArray[2] + "\r\n";
                result += "        /// </summary>\r\n";
                result += "        " + rows[i].ItemArray[1] + " = " + rows[i].ItemArray[0] + ",\r\n";
                if (i < rows.Count - 1)
                {
                    result += "\r\n";
                }
            }
            result += "    }\r\n";
            return result;
        }

        /// <summary>
        /// 生成Csharp枚举和掩码代码
        /// </summary>
        /// <param name="excel_path"></param>
        /// <param name="codepath"></param>
        private static void MakeNewCsharpEnumAndMask(string excel_path, string codepath)
        {
            List<string> files = new List<string>();
            files.AddRange(Directory.GetFiles(excel_path + "/Enum/", "*.xlsm", SearchOption.TopDirectoryOnly));
            files.AddRange(Directory.GetFiles(excel_path + "/Mask/", "*.xlsm", SearchOption.TopDirectoryOnly));
            ExcelHelper helper = new ExcelHelper();
            string path = codepath + "enum_types.cs";
            FileInfo csFile = new FileInfo(path);
            StreamWriter sw = csFile.CreateText();
            string content = "";
            content += "//THIS FILE IS GENERATED BY tabtool, DO NOT EDIT IT!\r\n";
            content += "//GENERATE TIME [" + DateTime.Now.ToString() + "]\r\n\r\n";
            content += "namespace config_enum_type\r\n";
            content += "{\r\n";
            for (int i = 0; i < files.Count; i++)
            {
                string filepath = files[i];
                try
                {
                    DataTable dt = helper.ImportExcelFile(filepath);
                    string filename = Path.GetFileNameWithoutExtension(filepath).ToLower();
                    content += GetCsharpEnumModel(filename, dt.Rows);
                    if (i < files.Count - 1)
                    {
                        content += "\r\n";
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(filepath + " 文件出错！");
                    Console.WriteLine(e.ToString());
                }
            }
            content += "}\r\n";
            sw.Write(content);
            sw.Close();
            Console.WriteLine("<生成C#枚举掩码文件成功>");
        }

        /// <summary>
        /// 生成Csharp结构体代码
        /// </summary>
        /// <param name="metalist"></param>
        /// <param name="codepath"></param>
        private static void MakeNewCsharpFileTbs(List<TableMeta> metalist, string codepath)
        {
            string hfile = codepath + "TableStruct.cs";
            FileInfo csFile = new FileInfo(hfile);
            StreamWriter sw = csFile.CreateText();
            string content = "";
            content += "//THIS FILE IS GENERATED BY tabtool, DO NOT EDIT IT!\r\n";
            content += "//GENERATE TIME [" + DateTime.Now.ToString() + "]\r\n\r\n";
            content += "namespace " + space_name + "\r\n";
            content += "{\r\n";
            for (int i = 0; i < metalist.Count; i++)
            {
                content += "    public class " + metalist[i].TableName + " : ITableObject\r\n";
                content += "    {\r\n";
                for (int j = 0; j < metalist[i].Fields.Count; j++)
                {
                    content += "        public " + metalist[i].Fields[j].GetCsharpTypeName() + " " + metalist[i].Fields[j].fieldName + " { get; private set; }\r\n";
                    content += "\r\n";
                }
                content += "        public void SetData(string data)\r\n";
                content += "        {\r\n";
                content += "            var vs = data.Split(',');\r\n";
                content += string.Format("            if (vs.Length < {0})\r\n", metalist[i].Fields.Count());
                content += "            {\r\n";
                content += "                return;\r\n";
                content += "            }\r\n";
                for (int j = 0; j < metalist[i].Fields.Count(); j++)
                {
                    var field = metalist[i].Fields[j];
                    switch (field.fieldType)
                    {
                        case TableFieldType.IntField:
                            content += string.Format("            {0} = DataReader.GetValue<int>(vs[{1}]);\r\n", field.fieldName, j);
                            break;
                        case TableFieldType.FloatField:
                            content += string.Format("            {0} = DataReader.GetValue<float>(vs[{1}]);\r\n", field.fieldName, j);
                            break;
                        case TableFieldType.StringField:
                            content += string.Format("            {0} = vs[{1}];\r\n", field.fieldName, j);
                            break;
                        default:
                            Console.WriteLine("{0}.{1}字段类型错误，只能是int float string！", metalist[i].TableName, field.fieldName);
                            break;
                    }
                }
                content += "        }\r\n";
                content += "    }\r\n";
                if (i < metalist.Count - 1)
                {
                    content += "\r\n";
                }
            }
            content += "}\r\n";
            sw.Write(content);
            sw.Close();
        }

        /// <summary>
        /// 关联代码模板
        /// </summary>
        /// <param name="meta"></param>
        /// <param name="field"></param>
        /// <returns></returns>
        private static string MakeNewCsharpRelate(TableMeta meta, TableField field)
        {
            string result = "";
            string relate_info = field.Get("relate");
            if (string.IsNullOrEmpty(relate_info))
            {
                return result;
            }
            string[] relates = relate_info.Split('|');
            foreach (var relate in relates)
            {
                string[] s = relate.Split('.');
                if (s.Count() != 2)
                {
                    throw new Exception(meta.TableName + " relate error!!! field name " + field.fieldName);
                }
                result += "\r\n";
                meta.relate = true;
                string valueName = string.Format("m_{0}Relate{1}{2}", field.fieldName, s[0], GetFisrtUpperString(s[1]));
                result += string.Format("        private List<td_{0}_item> {1};\r\n\r\n", s[0].ToLower(), valueName);
                result += string.Format("        public IReadOnlyList<td_{0}_item> List{1}Relate{2}{3}\r\n", s[0].ToLower(), GetFisrtUpperString(field.fieldName), s[0], GetFisrtUpperString(s[1]));
                result += "        {\r\n";
                result += "            get\r\n";
                result += "            {\r\n";
                result += string.Format("                if ({0} == null)\r\n", valueName);
                result += "                {\r\n";
                result += string.Format("                    {0} = new List<td_{1}_item>();\r\n", valueName, s[0].ToLower());
                result += string.Format("                    IReadOnlyList<td_{0}_item> all_td_{0}_item = ConfigManager.Instance.GetAllConfigByOrigin<td_{0}_item>();\r\n", s[0].ToLower());
                result += string.Format("                    for (int i = 0; i < all_td_{0}_item.Count; i++)\r\n", s[0].ToLower());
                result += "                    {\r\n";
                result += string.Format("                        if (all_td_{0}_item[i].{1} != {2})\r\n", s[0].ToLower(), s[1], field.fieldName);
                result += "                        {\r\n";
                result += "                            continue;\r\n";
                result += "                        }\r\n";
                result += string.Format("                        {0}.Add(all_td_{1}_item[i]);\r\n", valueName, s[0].ToLower());
                result += "                    }\r\n";
                result += "                }\r\n";
                result += string.Format("                return {0};\r\n", valueName);
                result += "            }\r\n";
                result += "        }\r\n";
            }
            meta.relate = true;
            return result;
        }

        /// <summary>
        /// 聚合代码模板
        /// </summary>
        /// <param name="meta"></param>
        /// <param name="field"></param>
        /// <returns></returns>
        public static string MakeNewCSharpRegroup(TableMeta meta, TableField field)
        {
            string result = "";
            string regroup_info = field.Get("regroup");
            if (string.IsNullOrEmpty(regroup_info))
            {
                return result;
            }
            result += "\r\n";
            meta.regroup = true;
            string valueName = string.Format("m_regroup{0}", GetFisrtUpperString(field.fieldName));
            result += string.Format("        private List<{0}> {1};\r\n\r\n", meta.GetItemName(), valueName);
            result += string.Format("        public IReadOnlyList<{0}> ListRegroup{1}\r\n", meta.GetItemName(), GetFisrtUpperString(field.fieldName));
            result += "        {\r\n";
            result += "            get\r\n";
            result += "            {\r\n";
            result += string.Format("                if ({0} == null)\r\n", valueName);
            result += "                {\r\n";
            result += string.Format("                    {0} = new List<{1}>();\r\n", valueName, meta.GetItemName());
            result += string.Format("                    IReadOnlyList<{0}> all_{0} = ConfigManager.Instance.GetAllConfigByClone<{0}>();\r\n", meta.GetItemName());
            result += string.Format("                    for (int i = 0; i < all_{0}.Count; i++)\r\n", meta.GetItemName());
            result += "                    {\r\n";
            result += string.Format("                        if (all_{0}[i].{1} != {1})\r\n", meta.GetItemName(), field.fieldName);
            result += "                        {\r\n";
            result += "                            continue;\r\n";
            result += "                        }\r\n";
            result += string.Format("                        {0}.Add(all_{1}[i]);\r\n", valueName, meta.GetItemName());
            result += "                    }\r\n";
            result += "                }\r\n";
            result += string.Format("                return {0};\r\n", valueName);
            result += "            }\r\n";
            result += "        }\r\n";
            return result;
        }

        /// <summary>
        /// 获取首字母大写的字符串（去除'_')
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private static string GetFisrtUpperString(string str)
        {
            string[] strArr = str.Split('_');
            string result = "";
            for (int i = 0; i < strArr.Length; i++)
            {
                strArr[i] = strArr[i].Substring(0, 1).ToUpper() + strArr[i].Substring(1);
                result += strArr[i];
            }
            return result;
        }

        /// <summary>
        /// 解析关键字模板
        /// </summary>
        /// <param name="fieldType"></param>
        /// <param name="field"></param>
        /// <returns></returns>
        private static string ParseValueModel(TableField field)
        {
            switch (field.fieldType)
            {
                case TableFieldType.EnumField:
                case TableFieldType.MaskField:
                case TableFieldType.IntField:
                    return string.Format("            {0} = DataReader.GetValue<int>(row[\"{0}\"].ToString());\r\n", field.fieldName);
                case TableFieldType.Int64Field:
                    return string.Format("            {0} = DataReader.GetValue<long>(row[\"{0}\"].ToString());\r\n", field.fieldName);
                case TableFieldType.FloatField:
                    return string.Format("            {0} = DataReader.GetValue<float>(row[\"{0}\"].ToString());\r\n", field.fieldName);
                case TableFieldType.DoubleField:
                    return string.Format("            {0} = DataReader.GetValue<double>(row[\"{0}\"].ToString());\r\n", field.fieldName);
                case TableFieldType.StringField:
                    return string.Format("            {0} = (row[\"{0}\"].ToString());\r\n", field.fieldName);
                case TableFieldType.TupleField:
                    //TODO:
                    return "";
                case TableFieldType.StructField:
                    return string.Format("            {0} = DataReader.GetObject<{1}>(row[\"{0}\"].ToString());\r\n", field.fieldName, field.GetCsharpTypeName());
                case TableFieldType.ListField:
                    if (field.subType == TableFieldType.StructField)
                    {
                        return string.Format("            {0} = DataReader.GetObjectList<{1}>(row[\"{0}\"].ToString());\r\n", field.fieldName, field.realType);
                    }
                    return string.Format("            {0} = DataReader.GetList<{1}>(\"{0}\");\r\n", field.fieldName, field.FieldTypeToString(field.subType));
                case TableFieldType.MapField:
                    return "";
                default:
                    return "";
            }
        }

        /// <summary>
        /// 生成内部数据
        /// </summary>
        /// <param name="metalist"></param>
        /// <param name="codepath"></param>
        private static void MakeNewCsharpFile(List<TableMeta> metalist, string codepath)
        {
            string initDataText = "";
            foreach (var meta in metalist)
            {
                initDataText += "            ConfigManager.Instance.Load<" + meta.GetItemName() + ">(\"" + meta.TableName + "\");\r\n";
                string csfile = codepath + meta.GetClassName() + ".cs";
                FileInfo fileInfo = new FileInfo(csfile);
                StreamWriter sw = fileInfo.CreateText();
                string content = "";
                content += "//THIS FILE IS GENERATED BY tabtool, DO NOT EDIT IT!\r\n";
                content += "//GENERATE TIME [" + DateTime.Now.ToString() + "]\r\n";
                content += "\r\n";
                content += "using System.Collections;\r\n";
                content += "using System.Collections.Generic;\r\n";
                content += "using System.Data;\r\n";
                content += "\r\n";
                string classContent = "";
                for (int i = 0; i < meta.Fields.Count; i++)
                {
                    var field = meta.Fields[i];
                    classContent += GetValueModel(field.commentsName, field.GetCsharpTypeName(), field.fieldName);
                    string relate_info = field.Get("relate");
                    string regroup_info = field.Get("regroup");
                    if (!string.IsNullOrEmpty(relate_info) && !string.IsNullOrEmpty(regroup_info))
                    {
                        throw new Exception(meta.TableName + " relate error!!! field name " + field.fieldName + "relate and regroup can`t both !!!");
                    }
                    classContent += MakeNewCsharpRelate(meta, field);
                    classContent += MakeNewCSharpRegroup(meta, field);
                    classContent += "\r\n";
                }
                classContent += "        public override int ID\r\n";
                classContent += "        {\r\n";
                classContent += "            get\r\n";
                classContent += "            {\r\n";
                classContent += "                return " + meta.Fields[0].fieldName + ";\r\n";
                classContent += "            }\r\n";
                classContent += "        }\r\n\r\n";
                classContent += "        public override void SetTable<T>(DataRow row)\r\n";
                classContent += "        {\r\n";
                foreach (var field in meta.Fields)
                {
                    classContent += ParseValueModel(field);
                }
                classContent += "        }\r\n";
                content += GetFileModel(meta.GetItemName(), classContent);
                sw.Write(content);
                sw.Close();
            }
            //写入初始化类中
            FileInfo initFile = new FileInfo(codepath + "InitData.cs");
            StreamWriter initSW = initFile.CreateText();
            string initContent = "";
            initContent += "//THIS FILE IS GENERATED BY tabtool, DO NOT EDIT IT!\r\n";
            initContent += "//GENERATE TIME [" + DateTime.Now.ToString() + "]\r\n\r\n";
            initContent += "namespace " + space_name + "\r\n";
            initContent += "{\r\n";
            initContent += "    public class InitData\r\n";
            initContent += "    {\r\n";
            initContent += "        public static void LoadAllConfig()\r\n";
            initContent += "        {\r\n";
            initContent += initDataText;
            initContent += "        }\r\n";
            initContent += "    }\r\n";
            initContent += "}\r\n";
            initSW.Write(initContent);
            initSW.Close();
        }

        /// <summary>
        /// 生成CSharp代码
        /// </summary>
        /// <param name="excel_path"></param>
        /// <param name="tbsList"></param>
        /// <param name="dataList"></param>
        /// <param name="codepath"></param>
        public static void MackCSharpCode(string excel_path, List<TableMeta> tbsList, List<TableMeta> dataList, string codepath)
        {
            MakeNewCsharpEnumAndMask(excel_path, codepath);
            MakeNewCsharpFileTbs(tbsList, codepath);
            MakeNewCsharpFile(dataList, codepath);
        }

        #endregion
    }
}